package cn.tedu.charging.device.service.impl;

import cn.tedu.charging.common.pojo.vo.StationInfoVO;
import cn.tedu.charging.common.utils.JsonUtils;
import cn.tedu.charging.device.constant.DeviceCacheKey;
import cn.tedu.charging.device.pojo.po.StationPO;
import cn.tedu.charging.device.service.StationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


@Slf4j
@Service
public class StationServiceImpl implements StationService {

    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;

    @Override
    public List<StationInfoVO> getNearStations(Double longitude, Double latitude, Double radius) {
        //BoundGeoOperations<String, Serializable> stations = redisTemplate.boundGeoOps("stations");
        BoundGeoOperations<String, Serializable> stations = redisTemplate.boundGeoOps(DeviceCacheKey.STATION_LOCATION_KEY);//14.4常量替换了下储存时的key名，换成了station:location

        ValueOperations<String, Serializable> valueOperations = redisTemplate.opsForValue();//14.3添加

        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();//14.5添加，测试hashmap方式的存和取

        //通过用户的经纬度创建RedisGEO的Point 用户的位置 点
        Point point = new Point(longitude, latitude);
        //通过用户传入的查找距离,理解为圆的半径
        Distance distance = new Distance(radius, Metrics.KILOMETERS);
        //通过点和半径 画圆
        Circle circle = new Circle(point, distance);
        //设置参数  还想在输入用户位置后，返回结果除了附件充电站的id，还有距离、经纬度，GEO中有其他参数args设置
        RedisGeoCommands.GeoRadiusCommandArgs args  = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        //设置参数 告诉Redis查询的时候返回距离
        args.includeDistance();
        //设置参数 告诉Redis查询的时候返回经纬度
        args.includeCoordinates();

        //查到的返回结果是列表，要遍历
        //GeoResults<RedisGeoCommands.GeoLocation<Serializable>> geoResults = stations.radius(circle);
        //通过Redis radius 方法查询 附近的充电站 调用的是 Redis GEORADIUS 这个命令
        GeoResults<RedisGeoCommands.GeoLocation<Serializable>> geoResults = stations.radius(circle,args);//多了参数
        log.debug("通过经度:{},维度:{},距离:{} 查询附近的充电站:{}",longitude,latitude,radius,geoResults);

        List<StationInfoVO> stationInfoVOS = new ArrayList<>();
        for (GeoResult<RedisGeoCommands.GeoLocation<Serializable>> geoResult : geoResults) {
            //name是指站点的id，StationDataInit中遍历存放pointMap.put(String.valueOf(stationPO.getId()),point);存放的key就是站点id
            //content里的name属性 是缓存预热保存 Point 的那个map的key，value 是 point 站点id        day14上午181分钟
            //Serializable name = geoResult.getContent().getName();//没办法，这个方法就叫getName
            Serializable stationId = geoResult.getContent().getName();
            //获取充电站和入参位置经纬度 的距离
            double stationDistance = geoResult.getDistance().getValue();
            //获取充电站的位置 经纬度
            Point stationPoint = geoResult.getContent().getPoint();

            /*StationInfoVO stationInfoVO = new StationInfoVO();
            stationInfoVO.setStationId(Integer.valueOf(String.valueOf(name)));

            stationInfoVOS.add(stationInfoVO);*/    //14.3改进，要保存其他基本信息，如下
            //String stationInfoKey = "station:info:" + name;
            //通过 保存为Redis String类型获取场站信息
            String stationInfoKey = DeviceCacheKey.STATION_DETAIL_STRING_KEY + stationId;
            Serializable serializable = valueOperations.get(stationInfoKey);

            //14.5这段代码，测试hashmap方式的取
            String stationInfoHashBigKey = DeviceCacheKey.STATION_DETAIL_HASH_KEY  + stationId;
            String stationInfoHashSmallKey = "name";
            Object object = hashOperations.get(stationInfoHashBigKey, stationInfoHashSmallKey);
            log.debug("通过大key:{},小key:{},获取小map里具体的 name 的值 {},只能获取一个",stationInfoHashBigKey,stationInfoHashSmallKey,object);
            //entries redis 命令 HGETALL 推荐用一次命令全部获取
            Map<Object, Object> entries = hashOperations.entries(stationInfoHashBigKey);
            log.debug("通过大key:{},获取小map所有值 {},全部获取,推荐使用",stationInfoHashBigKey,entries);
            Object name = entries.get("name");
            Object deviceNumber = entries.get("deviceNumber");
            Object operatorId = entries.get("operatorId");
            log.debug("通过大key:{},获取小map所有值,通过小map的get方法从map里获取具体的值,name:{},deviceNumber:{},operatorId:{}",name,deviceNumber,operatorId);


            if (serializable != null) {
                StationPO stationPO = JsonUtils.fromJson(String.valueOf(serializable), StationPO.class);
                if (stationPO != null) {
                    StationInfoVO stationInfoVO = new StationInfoVO();
                    //stationInfoVO.setStationId(Integer.valueOf(String.valueOf(name)));
                    stationInfoVO.setStationId(Integer.valueOf(String.valueOf(stationId)));
                    //通过String方式获取
                    stationInfoVO.setStationName(stationPO.getStationName());
                    //stationInfoVO.setStationName(String.valueOf(name));//通过hash方式获取，注意给前端不返回相同的值2次，防止前端混乱了，所以先注释了

                    stationInfoVO.setOperatorId(stationPO.getOperatorId());
                    stationInfoVO.setDeviceNumber(stationPO.getDeviceNumber());
                    //设置距离
                    stationInfoVO.setDistance(stationDistance);
                    //设置经度
                    stationInfoVO.setLongitude(stationPoint.getX());
                    //设置维度
                    stationInfoVO.setLatitude(stationPoint.getY());

                    stationInfoVOS.add(stationInfoVO);
                }
            }
        }
        return stationInfoVOS;
    }

    @Override   //16.1添加
    public StationInfoVO getStationDetail(Integer stationId) {
        ValueOperations<String, Serializable> valueOperations = redisTemplate.opsForValue();
        String stationInfoKey = DeviceCacheKey.STATION_DETAIL_STRING_KEY + stationId;
        Serializable serializable = valueOperations.get(stationInfoKey);
        if (serializable != null) {
            StationPO stationPO = JsonUtils.fromJson(String.valueOf(serializable), StationPO.class);
            if (stationPO != null) {
                StationInfoVO stationInfoVO = new StationInfoVO();
                stationInfoVO.setStationId(Integer.valueOf(String.valueOf(stationId)));
                //通过String方式获取
                stationInfoVO.setStationName(stationPO.getStationName());
                //通过hash方式获取
                //stationInfoVO.setStationName(String.valueOf(name));
                stationInfoVO.setOperatorId(stationPO.getOperatorId());
                stationInfoVO.setDeviceNumber(stationPO.getDeviceNumber());
                return stationInfoVO;
            }
        }
        return new StationInfoVO();
    }
}
